package org.sdnstproject.interlayer.interlayerProxy;

import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Longs;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import org.projectfloodlight.openflow.exceptions.OFParseError;
import org.projectfloodlight.openflow.protocol.*;
import org.projectfloodlight.openflow.types.OFHelloElement;
import org.projectfloodlight.openflow.types.U16;
import org.projectfloodlight.openflow.types.U32;
import org.sdnstproject.interlayer.OF13Protocal.FuAsyncGetRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuAsyncSet;
import org.sdnstproject.interlayer.OF13Protocal.FuBarrierRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuDescStatsRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuEchoReply;
import org.sdnstproject.interlayer.OF13Protocal.FuEchoRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuError;
import org.sdnstproject.interlayer.OF13Protocal.FuFeaturesReply;
import org.sdnstproject.interlayer.OF13Protocal.FuFeaturesRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuFlowMod;
import org.sdnstproject.interlayer.OF13Protocal.FuFlowStatsRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuGetConfigRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuGroupMod;
import org.sdnstproject.interlayer.OF13Protocal.FuHello;
import org.sdnstproject.interlayer.OF13Protocal.FuMeterFeaturesStatsRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuMeterMod;
import org.sdnstproject.interlayer.OF13Protocal.FuMeterStatsRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuPacketOut;
import org.sdnstproject.interlayer.OF13Protocal.FuPortDescStatsRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuPortMod;
import org.sdnstproject.interlayer.OF13Protocal.FuPortStatsRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuQueueGetConfigRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuRoleRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuSetConfig;
import org.sdnstproject.interlayer.OF13Protocal.FuTableFeaturesStatsRequest;
import org.sdnstproject.interlayer.OF13Protocal.FuTableMod;
import org.sdnstproject.interlayer.interlayerProxy.DummySwitch.OFState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.sound.midi.MidiDevice.Info;

public class DummyController extends Thread {
    private static final Logger log = LoggerFactory.getLogger(DummyController.class);

    public static final int HANDSHAKE_DEFAULT = 0;
    public static final int HANDSHAKE_NO_HELLO = 1;
    public static final int HANDSHAKE_INCOMPATIBLE_HELLO = 2;
    public static final int NO_HANDSHAKE = 3;

    public static final int MINIMUM_LENGTH = 8;

    public ServerSocket serverSock;
    public Socket targetSock;
    private InputStream in;
    private OutputStream out;
    /* for OF message */
    private OFVersion version = OFVersion.OF_13;
    private OFFactory factory = OFFactories.getFactory(OFVersion.OF_13);;
    private OFMessageReader<OFMessage> reader = factory.getReader();
    private int handShakeType;

    private volatile long requestXid = 0xeeeeeeeel;
    public volatile long responseXid = 0x0L;
    private long startXid = 0xffffffff;
    private long cntXid = 1;
    private boolean handshaked = false;
    private boolean synack = false;
    private Configuration config = Configuration.getInstance();
    private int port = Integer.parseInt(config.getInterlayerPort());
    private OFFlowAdd backupFlowAdd;
    public volatile boolean strategySend = false;
    public volatile boolean running = true;
    private OFState state = new OFState();
    private Strategy strategy;
    private ResultAnalyzer analyzer = ResultAnalyzer.getInstance();
    private HashSet<OFType> needSynMsg = new HashSet<OFType>();

    public DummyController() {
    	
    }
	public DummyController(Strategy strategy) {
        this.strategy =  strategy;
    }
	
	public void notifyOFFuzzer() {
    	strategySend = true;
    	synchronized (this) {
			notify();
		}
    }
	
	@Override
    public void run() {
        byte[] recv;
        int readlen;
        try {
            while (running && !Thread.currentThread().isInterrupted()) {
                recv = new byte[8192];
                int mlen;
                int len = 0;
                if ((readlen = in.read(recv, 0, recv.length)) != -1) {
                    byte[] lenarr = {recv[2], recv[3]};
                    mlen = Integer.parseInt(javax.xml.bind.DatatypeConverter.printHexBinary(lenarr), 16);
                    ByteBuf newrecv = Unpooled.directBuffer(mlen);
                    newrecv.writeBytes(recv);
                    len = len + readlen;
                    newrecv.writerIndex(len);

                    while(mlen > len){
                        if((readlen = in.read(recv, 0, recv.length)) != -1){
                            newrecv.writeBytes(recv);
                            len = len + readlen;
                            newrecv.writerIndex(len);
                        }

                    }

                    parseOFMsg(newrecv, mlen);
//                    parseOFMsg(recv, readlen);
                } else {
                    in.close();
                    out.close();
                    serverSock.close();
                    targetSock.close();
                    break; // end of connection
                }
            }
        } catch (Exception e) {
            currentThread().interrupt();
            e.printStackTrace();
        } finally {
            try {
                in.close();
                out.close();
                serverSock.close();
                targetSock.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

	public boolean parseOFMsg(ByteBuf bb, int len) throws OFParseError {
        int totalLen = bb.readableBytes();
        int offset = bb.readerIndex();
        while (offset < totalLen) {
            bb.readerIndex(offset);

            bb.readByte();
            bb.readByte();
            int length = U16.f(bb.readShort());
            bb.readerIndex(offset);

            if (length < MINIMUM_LENGTH)
                throw new OFParseError("Wrong length: Expected to be >= " + MINIMUM_LENGTH + ", was: " + length);

            OFMessage message = reader.readFrom(bb);
            responseXid = message.getXid();
            //if get correct response xid, then notify the state. 
            if(responseXid == requestXid) {
            	synchronized (state) {
    				state.notify();
    			}
            }
            //process OpenFlow message.
            state.processOFMsg(message);
            
            offset += length;
        }

        bb.clear();
        return true;
    }
	
	public class OFState extends Thread{
    	void processOFMsg(OFMessage m) {
    		switch(m.getType()) {
			case HELLO:
				processOFHello((OFHello)m);
				break;
			case FEATURES_REPLY:
				processOFFeaturesReply((OFFeaturesReply)m);
				break;
			case GET_CONFIG_REPLY:
				processOFGetConfigReply((OFGetConfigReply)m);
				break;
			case BARRIER_REPLY:
				processOFBarrierReply((OFBarrierReply)m);
				break;
			case STATS_REPLY:
				processOFStatsReply((OFStatsReply)m);
				break;
			case EXPERIMENTER:
				processOFExperimenter((OFExperimenter)m);
				break;
			case ECHO_REQUEST:
				processOFEchoRequest((OFEchoRequest)m);
				break;
			case ECHO_REPLY:
				processOFEchoReply((OFEchoReply)m);
				break;
			case ERROR:
				processOFError((OFErrorMsg)m);
				break;
			case FLOW_REMOVED:
				processOFFlowRemoved((OFFlowRemoved)m);
				break;
			case ROLE_REPLY:
				processOFRoleReply((OFRoleReply)m);
				break;
			case PACKET_IN:
				processOFPacketIn((OFPacketIn)m);
				break;
			case PORT_STATUS:
				processOFPortStatus((OFPortStatus)m);
				break;
			}
    		// print message.
    		log.info(m.toString());
    	}
    	
    	private void processOFPortStatus(OFPortStatus m) {
    		log.info("Reveived a OFPortStatus.");
		}

		private void processOFPacketIn(OFPacketIn m) {
			log.info("Reveived a OFPacketIn.");
		}

		private void processOFRoleReply(OFRoleReply m) {
			log.info("Reveived a OFRoleReply.");
		}

		private void processOFFlowRemoved(OFFlowRemoved m) {
			log.info("Reveived a OFFlowRemoved.");
		}

		private void processOFError(OFErrorMsg m) {
			log.info("Reveived a OFErrorMsg.");
		}

		private void processOFEchoReply(OFEchoReply m) {
			log.info("Reveived a OFEchoReply.");
		}

		private void processOFEchoRequest(OFEchoRequest m) {
			log.info("Reveived a OFEchoRequest.");
			if(strategy != null && strategy.ofType == OFType.ECHO_REPLY) {
	    		FuEchoReply echoReply = new FuEchoReply();
	    		echoReply.setField(strategy.field, strategy.actionValue);
	    		sendRawMsg(echoReply.writeTo());
	    		notifyOFFuzzer();
	    	}else {
	    		sendEchoReply(m.getXid());
	    	}
		}

		private void processOFExperimenter(OFExperimenter m) {
			log.info("Reveived a OFExperimenter.");	
			sendExperimenter(m.getXid());
		}

		private void processOFStatsReply(OFStatsReply m) {
			log.info("Reveived a OFStatsReply.");			
		}

		private void processOFGetConfigReply(OFGetConfigReply m) {
			log.info("Reveived a OFGetConfigReply.");	
		}

		private void processOFBarrierReply(OFBarrierReply m) {
			log.info("Reveived a OFGetConfigReply.");	
		}

		private void processOFFeaturesReply(OFFeaturesReply m) {
			handshaked = true;
    		if(strategy != null && needSynMsg.contains(strategy.ofType)) {
    			state.start();
    			log.info("asynchronous state machine has start.");
    		}
		}

		private void processOFHello(OFHello m) {
			//send hello
			if(strategy != null && strategy.ofType == OFType.HELLO) {
	    		FuHello hello = new FuHello();
	    		hello.setField(strategy.field, strategy.actionValue);
	    		sendRawMsg(hello.writeTo());
	    		notifyOFFuzzer();
	    	}else {
	    		sendHello(0);
	    	}
			
			//send FeatureReply
			if(strategy != null && strategy.ofType == OFType.FEATURES_REQUEST) {
    			FuFeaturesRequest featuresRequest = new FuFeaturesRequest();
    			featuresRequest.xid = requestXid;
    			featuresRequest.setField(strategy.field, strategy.actionValue);
    			sendRawMsg(featuresRequest.writeTo());
        		notifyOFFuzzer();
        	}else {
        		try {
					sendFeatureReq(requestXid);
				} catch (OFParseError e) {
					e.printStackTrace();
				}
        	}
		}

		@Override
		public void run() {
			switch (strategy.ofType) {
			case PACKET_OUT:
				sendPACKETOUTtoController();
				break;
			case ECHO_REQUEST:
				sendECHOREQUESTtoController();
				break;
			case FLOW_MOD:
				sendFLOWMODtoController();
				break;
			case GROUP_MOD:
				sendGROUPMODtoController();
				break;
			case PORT_MOD:
				sendPORTMODtoController();
				break;
			case METER_MOD:
				sendMeterMODtoController();
				break;
			case STATS_REQUEST:
				sendStatsRequesttoController();
				break;
			case QUEUE_GET_CONFIG_REQUEST:
				sendQueueGetConfigRequesttoController();
				break;
			case TABLE_MOD:
				sendTABLEMODtoController();
				break;
			case GET_CONFIG_REQUEST:
				sendGetConfigRequesttoController();
				break;
			case SET_ASYNC:
				sendSETASYNCtoController();
				break;
			case SET_CONFIG:
				sendSETCONFIGtoController();
				break;
			case GET_ASYNC_REQUEST:
				sendGETASYNCREQUESTtoController();
				break;
			case ERROR:
				sendERRORtoController();
				break;
			case ROLE_REQUEST:
				sendRoleRequesttoController();
				break;
			case BARRIER_REQUEST:
				sendBarrierRequesttoController();
				break;
			}
			strategySend = true;
			//after send syn stratege msg. waitting 3sec to get response msg or waitting to been notified.
			synchronized (this) {
				if(responseXid != requestXid) {
					try {
						wait(3000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			if(responseXid == requestXid) {
				log.info("responseXid == requestXid");
				analyzer.notReceiveResponseMsg = false;
			}else {
				log.info("responseXid != requestXid");
				analyzer.notReceiveResponseMsg = true;
			}
			//notify ResultAnalyzer to get result.
			notifyOFFuzzer();
		}
    }
	
	 public void sendPACKETOUTtoController() {
		 FuPacketOut packetOut = new FuPacketOut();
		 packetOut.xid = requestXid;
		 packetOut.setField(strategy.field, strategy.actionValue);
		 sendRawMsg(packetOut.writeTo());
 		 notifyOFFuzzer();
	}
	
    public void sendBarrierRequesttoController() {
    	FuBarrierRequest barrierRequest = new FuBarrierRequest();
    	barrierRequest.xid = requestXid;
    	barrierRequest.setField(strategy.field, strategy.actionValue);
		sendRawMsg(barrierRequest.writeTo());
		notifyOFFuzzer();
	}
	public void sendRoleRequesttoController() {
    	FuRoleRequest roleRequest = new FuRoleRequest();
    	roleRequest.xid = requestXid;
    	roleRequest.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(roleRequest.writeTo());
    	notifyOFFuzzer();
	}
    
	public void sendStatsRequesttoController() {
		switch (strategy.childType) {
		case "PORT_DESC_REQUEST":
			FuPortDescStatsRequest portDescStatsRequest = new FuPortDescStatsRequest();
			portDescStatsRequest.xid = requestXid;
			portDescStatsRequest.setField(strategy.field, strategy.actionValue);
	    	sendRawMsg(portDescStatsRequest.writeTo());
	    	notifyOFFuzzer();
			break;
		case "DESC_REQUEST":
			FuDescStatsRequest descStatsRequest = new FuDescStatsRequest();
			descStatsRequest.xid = requestXid;
			descStatsRequest.setField(strategy.field, strategy.actionValue);
	    	sendRawMsg(descStatsRequest.writeTo());
	    	notifyOFFuzzer();
			break;
		case "METER_REQUEST":
			FuMeterStatsRequest meterStatsRequest = new FuMeterStatsRequest();
			meterStatsRequest.xid = requestXid;
			meterStatsRequest.setField(strategy.field, strategy.actionValue);
	    	sendRawMsg(meterStatsRequest.writeTo());
	    	notifyOFFuzzer();
			break;
		case "PORT_REQUEST":
			FuPortStatsRequest portStatsRequest = new FuPortStatsRequest();
			portStatsRequest.xid = requestXid;
			portStatsRequest.setField(strategy.field, strategy.actionValue);
	    	sendRawMsg(portStatsRequest.writeTo());
	    	notifyOFFuzzer();
			break;
		case "FLOW_REQUEST":
			FuFlowStatsRequest flowStatsRequest = new FuFlowStatsRequest();
			flowStatsRequest.xid = requestXid;
			flowStatsRequest.setField(strategy.field, strategy.actionValue);
	    	sendRawMsg(flowStatsRequest.writeTo());
	    	notifyOFFuzzer();
			break;
		case "METER_FEATURES_REQUEST":
			FuMeterFeaturesStatsRequest meterFeaturesStatsRequest = new FuMeterFeaturesStatsRequest();
			meterFeaturesStatsRequest.xid = requestXid;
			meterFeaturesStatsRequest.setField(strategy.field, strategy.actionValue);
	    	sendRawMsg(meterFeaturesStatsRequest.writeTo());
	    	notifyOFFuzzer();
			break;
		case "TABLE_FEATURES_REQUEST":
			FuTableFeaturesStatsRequest tableFeaturesStatsRequest = new FuTableFeaturesStatsRequest();
			tableFeaturesStatsRequest.xid = requestXid;
			tableFeaturesStatsRequest.setField(strategy.field, strategy.actionValue);
	    	sendRawMsg(tableFeaturesStatsRequest.writeTo());
	    	notifyOFFuzzer();
			break;
		}
	}
	
	public void sendQueueGetConfigRequesttoController() {
		FuQueueGetConfigRequest queueGetConfigRequest = new FuQueueGetConfigRequest();
		queueGetConfigRequest.xid = requestXid;
		queueGetConfigRequest.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(queueGetConfigRequest.writeTo());
    	notifyOFFuzzer();
		
	}
	
	public void sendMeterMODtoController() {
		FuMeterMod meterMod = new FuMeterMod();
		meterMod.xid = requestXid;
		meterMod.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(meterMod.writeTo());
    	notifyOFFuzzer();
	}
	
	public void sendGetConfigRequesttoController() {
		FuGetConfigRequest getConfigRequest = new FuGetConfigRequest();
		getConfigRequest.xid = requestXid;
		getConfigRequest.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(getConfigRequest.writeTo());
    	notifyOFFuzzer();
	}
	
	public void sendSETCONFIGtoController() {
		FuSetConfig setConfig = new FuSetConfig();
		setConfig.xid = requestXid;
		setConfig.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(setConfig.writeTo());
    	notifyOFFuzzer();
	}
	
	public void sendSETASYNCtoController() {
		FuAsyncSet asyncSet = new FuAsyncSet();
		asyncSet.xid = requestXid;
		asyncSet.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(asyncSet.writeTo());
    	notifyOFFuzzer();
	}
	
	public void sendGETASYNCREQUESTtoController() {
		FuAsyncGetRequest asyncGetRequest = new FuAsyncGetRequest();
		asyncGetRequest.xid = requestXid;
		asyncGetRequest.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(asyncGetRequest.writeTo());
    	notifyOFFuzzer();
	}
	
	public void sendTABLEMODtoController() {
		FuTableMod tableMod = new FuTableMod();
		tableMod.xid = requestXid;
		tableMod.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(tableMod.writeTo());
    	notifyOFFuzzer();
	}
	
	public void sendPORTMODtoController() {
		FuPortMod portMod = new FuPortMod();
		portMod.xid = requestXid;
		portMod.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(portMod.writeTo());
    	notifyOFFuzzer();
	}
	
	public void sendGROUPMODtoController() {
		FuGroupMod groupMod = new FuGroupMod();
		groupMod.xid = requestXid;
		groupMod.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(groupMod.writeTo());
    	notifyOFFuzzer();
	}
	
	public void sendERRORtoController() {
		FuError error = new FuError();
		error.xid = requestXid;
		error.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(error.writeTo());
    	notifyOFFuzzer();
	}
	
	public void sendFLOWMODtoController() {
		FuFlowMod flowMod = new FuFlowMod();
		flowMod.xid = requestXid;
		flowMod.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(flowMod.writeTo());
    	notifyOFFuzzer();
	}
	
	public void sendECHOREQUESTtoController() {
		FuEchoRequest echoRequest = new FuEchoRequest();
		echoRequest.xid = requestXid;
		echoRequest.setField(strategy.field, strategy.actionValue);
    	sendRawMsg(echoRequest.writeTo());
    	notifyOFFuzzer();
	}
   
	public void sendMsg(OFMessage msg, int len) {
        ByteBuf buf;

        if (len == -1) {
            buf = PooledByteBufAllocator.DEFAULT.directBuffer(1024);
        } else {
            buf = PooledByteBufAllocator.DEFAULT.directBuffer(len);
        }

        msg.writeTo(buf);

        int length = buf.readableBytes();
        byte[] bytes = new byte[length];
        buf.getBytes(buf.readerIndex(), bytes);

        try {
            this.out.write(bytes, 0, length);
        } catch (IOException e) {
            e.printStackTrace();
        }

        buf.clear();
        buf.release();
    }

    public void sendRawMsg(byte[] msg) {
        try {
            this.out.write(msg, 0, msg.length);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void sendRawMsg(ByteBuf buf) {
        int length = buf.readableBytes();
        byte[] bytes = new byte[length];
        buf.getBytes(buf.readerIndex(), bytes);

        try {
            this.out.write(bytes, 0, length);
        } catch (IOException e) {
            e.printStackTrace();
        }

        buf.clear();
        buf.release();
    }

    public OFFlowAdd getBackupFlowAdd() {
        return backupFlowAdd;
    }

    public ByteBuf sendFlowRemoved() throws OFParseError {
        OFFactory factory = OFFactories.getFactory(OFVersion.OF_10);
        long r_xid = 0xeeeeeeeel;

        OFFlowRemoved.Builder fab = factory.buildFlowRemoved();
        fab.setXid(r_xid);
        OFFlowRemoved hello = fab.build();

        ByteBuf buf = null;
        buf = PooledByteBufAllocator.DEFAULT.directBuffer(88);
        hello.writeTo(buf);

        byte[] bytes;
        int length = buf.readableBytes();
        bytes = new byte[length];
        buf.getBytes(buf.readerIndex(), bytes);

        try {
            this.out.write(bytes, 0, length);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return buf;
    }

    public ByteBuf sendPortStatus() throws OFParseError {
        OFFactory factory = OFFactories.getFactory(OFVersion.OF_10);
        long r_xid = 0xeeeeeeeel;

        OFPortStatus.Builder fab = factory.buildPortStatus();
        fab.setXid(r_xid);
        OFPortStatus hello = fab.build();

        ByteBuf buf = null;
        buf = PooledByteBufAllocator.DEFAULT.directBuffer(64);
        hello.writeTo(buf);

        byte[] bytes;
        int length = buf.readableBytes();
        bytes = new byte[length];
        buf.getBytes(buf.readerIndex(), bytes);

        try {
            this.out.write(bytes, 0, length);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return buf;
    }

    public void listeningSwitch() throws Exception {
        log.info("[DummyController] Listening switches on " + this.port);
        serverSock = new ServerSocket(port);
        serverSock.setReuseAddress(true);
        targetSock = serverSock.accept();
        log.info("[DummyController] Switch connected from  " + targetSock.toString());
        in = targetSock.getInputStream();
        out = targetSock.getOutputStream();
    }

    public void sendExperimenter(long xid) {
        byte[] msg = DummyData.hexStringToByteArray(DummyData.VENDOR);
        byte[] xidbytes = Longs.toByteArray(xid);
        System.arraycopy(xidbytes, 4, msg, 4, 4);

        if (this.version == OFVersion.OF_13) {
            msg[0] = (byte) 0x04;
        }

        sendRawMsg(msg);
    }

    public void sendStatReq(long xid) throws OFParseError {
        if (this.version == OFVersion.OF_10) {
            byte[] msg = DummyData.hexStringToByteArray(DummyData.STATS_REQ);
            byte[] xidbytes = Longs.toByteArray(xid);
            System.arraycopy(xidbytes, 4, msg, 4, 4);

            sendRawMsg(msg);
            return;
        } else if (this.version == OFVersion.OF_13) {
            OFAggregateStatsRequest.Builder builder = factory.buildAggregateStatsRequest();
            Set<OFStatsRequestFlags> flagset = ImmutableSet.<OFStatsRequestFlags>of();

            builder.setFlags(flagset);
            builder.setXid(xid);

            sendMsg(builder.build(), -1);
        }
    }

    public void sendSetConfig(long xid) throws OFParseError {
        OFSetConfig.Builder builder = factory.buildSetConfig();
        builder.setMissSendLen(0xffff);
        builder.setXid(xid);

        sendMsg(builder.build(), -1);
    }

    public void sendGetConfigReq(long xid) throws OFParseError {
        OFGetConfigRequest.Builder builder = factory.buildGetConfigRequest();
        builder.setXid(xid);

        sendMsg(builder.build(), -1);
    }

    public void sendFeatureReq(long xid) throws OFParseError {
        OFFeaturesRequest.Builder builder = factory.buildFeaturesRequest();
        builder.setXid(xid);

        sendMsg(builder.build(), -1);
    }

    public void sendEchoReply(long xid) {
        OFEchoReply.Builder builder = factory.buildEchoReply();
        builder.setXid(xid);

        sendMsg(builder.build(), -1);
    }

    public void sendHello(long xid) {
        OFHello.Builder fab = factory.buildHello();
        fab.setXid(xid);

        if (version != OFVersion.OF_10) {
            OFHelloElem.Builder heb = factory.buildHelloElemVersionbitmap();
            ((OFHelloElemVersionbitmap.Builder) heb).setBitmaps(Collections.singletonList(U32.of(0x12)));
            List<OFHelloElem> list = new ArrayList<OFHelloElem>();
            list.add(heb.build());
            fab.setElements(list);
        }

        OFHello hello = fab.build();
        sendMsg(hello, -1);
    }
    
    private void addSynMsg() {
    	needSynMsg.add(OFType.METER_MOD);
    	needSynMsg.add(OFType.PACKET_OUT);
    	needSynMsg.add(OFType.FLOW_MOD);
    	needSynMsg.add(OFType.GROUP_MOD);
    	needSynMsg.add(OFType.TABLE_MOD);
    	needSynMsg.add(OFType.SET_CONFIG);
    	needSynMsg.add(OFType.SET_ASYNC);
    	needSynMsg.add(OFType.BARRIER_REQUEST);
    	needSynMsg.add(OFType.GET_ASYNC_REQUEST);
    	needSynMsg.add(OFType.GET_CONFIG_REQUEST);
    	needSynMsg.add(OFType.QUEUE_GET_CONFIG_REQUEST);
    	needSynMsg.add(OFType.ECHO_REQUEST);
    	needSynMsg.add(OFType.ERROR);
    	needSynMsg.add(OFType.EXPERIMENTER);
    	needSynMsg.add(OFType.STATS_REQUEST);
    	needSynMsg.add(OFType.ROLE_REQUEST);
    }

}
